package lxs.swift.verify;

import lxs.swift.interfaces.Defaultable;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;

public class Verifies {

    /**
     * 判断对象是否为默认值
     * Byte|Short|Integer = 0
     * Long = 0L
     * BigInteger = BigInteger.ZERO
     * Float = 0.0f
     * Double = 0.0
     * BigDecimal = BigDecimal.ZERO
     * Boolean = false
     * Character = (char) 0
     * Collection.isEmpty()
     * [].length = 0
     * CharSequence.length() = 0
     * Map.isEmpty()
     * Object 判断所有非静态字段是否为默认值
     */
    public static boolean isDefault(@NotNull Object o) {
        Objects.requireNonNull(o);
        if (o instanceof CharSequence) return ((CharSequence) o).length() == 0;
        if (o instanceof Byte) return o.equals(((byte) 0));
        if (o instanceof Short) return o.equals(((short) 0));
        if (o instanceof Integer) return o.equals(0);
        if (o instanceof Long) return o.equals(0L);
        if (o instanceof Float) return o.equals(0f);
        if (o instanceof Double) return o.equals(0.0);
        if (o instanceof Boolean) return o.equals(false);
        if (o instanceof Character) return o.equals(((char) 0));
        if (o instanceof BigInteger) return o.equals(BigInteger.ZERO);
        if (o instanceof BigDecimal) return o.equals(BigDecimal.ZERO);
        if (o instanceof Number) {
            Number num = (Number) o;
            return num.longValue() == 0L && num.doubleValue() == 0.0;
        }
        if (o instanceof Collection) {
            return ((Collection<?>) o).isEmpty();
        }
        if (o instanceof Map) {
            return ((Map<?, ?>) o).isEmpty();
        }
        if (o instanceof Defaultable) {
            return ((Defaultable) o).isDefault();
        }
        if (o.getClass().isArray()) {
            assert o instanceof Object[];
            return ((Object[]) o).length == 0;
        }
        Class<?> aClass = o.getClass();
        for (Field field : aClass.getDeclaredFields()) {
            int modifiers = field.getModifiers();
            if (Modifier.isStatic(modifiers)) {
                continue;
            }
            field.setAccessible(true);
            try {
                Object f = field.get(o);
                if (isDefault(f)) continue;
                return false;
            } catch (IllegalAccessException e) {
                continue;
            }
        }
        return false;
    }

    /**
     * 判断对象是否为空或者为默认值
     */
    public static boolean isEmpty(Object o) {
        if (o == null) return true;
        return isDefault(o);
    }

    public static boolean isNotEmpty(Object o) {
        if (o == null) return false;
        return !isDefault(o);
    }

    public static <T> boolean isObjectEq(T o1, T o2) {
        if (o1 == null) return o2 == null;
        return o1.equals(o2);
    }

    public static boolean isEq(Boolean o1, Boolean o2) {
        if (o1 == null) return o2 == null;
        return o1.equals(o2);
    }

    public static boolean isEq(CharSequence o1, CharSequence o2) {
        if (o1 == null) return o2 == null;
        return o1.equals(o2);
    }

    public static boolean isEq(Integer o1, Integer o2) {
        if (o1 == null) return o2 == null;
        return o1.equals(o2);
    }

    public static boolean isEq(Float o1, Float o2) {
        if (o1 == null) return o2 == null;
        return o1.equals(o2);
    }

    public static boolean isEq(Double o1, Double o2) {
        if (o1 == null) return o2 == null;
        return o1.equals(o2);
    }

    public static boolean isEq(BigDecimal o1, BigDecimal o2) {
        if (o1 == null) return o2 == null;
        return o1.equals(o2);
    }

    public static boolean isEq(BigInteger o1, BigInteger o2) {
        if (o1 == null) return o2 == null;
        return o1.equals(o2);
    }

    public static boolean isNull(Object o) {
        return o == null;
    }

    public static boolean isNotNull(Object o) {
        return o != null;
    }

    public static <T extends Comparable<T>> boolean lt(T v1, T v2, @NotNull T initIfNull) {
        Objects.requireNonNull(initIfNull);
        if (v1 == null) {
            v1 = initIfNull;
        }
        if (v2 == null) {
            v2 = initIfNull;
        }
        return v1.compareTo(v2) < 0;
    }

    public static <T extends Comparable<T>> boolean gt(T v1, T v2, @NotNull T initIfNull) {
        Objects.requireNonNull(initIfNull);
        if (v1 == null) {
            v1 = initIfNull;
        }
        if (v2 == null) {
            v2 = initIfNull;
        }
        return v1.compareTo(v2) > 0;
    }

    public static <T extends Comparable<T>> boolean gte(T v1, T v2, @NotNull T initIfNull) {
        return !lt(v1, v2, initIfNull);
    }

    public static <T extends Comparable<T>> boolean lte(T v1, T v2, @NotNull T initIfNull) {
        return !gt(v1, v2, initIfNull);
    }

    public static <T extends Comparable<T>> boolean between(T v, @NotNull T left, @NotNull T right) {
        if (v == null) return false;
        if (left.compareTo(right) > 0) {
            T temp = left;
            left = right;
            right = temp;
        }
        int l = v.compareTo(left);
        int r = v.compareTo(right);
        return l >= 0 && r <= 0;
    }

    public static <T> boolean include(T value, @NotNull Collection<T> items) {

        Objects.requireNonNull(items);
        return items.contains(value);
    }

    @SafeVarargs
    public static <T> boolean include(T value, @NotNull T... items) {
        Objects.requireNonNull(items);
        return include(value, Arrays.asList(items));
    }


    public static <T> boolean exclude(T value, @NotNull Collection<T> items) {
        return !include(value, items);
    }

    @SafeVarargs
    public static <T> boolean exclude(T value, @NotNull T... items) {
        return !include(value, items);
    }

}
